<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>ECMAScript 引用类型 prototype</title>
    <script>
        // 我们主要是写为什么需要 prototype 到底在哪些场景下使用
        /*
        简单理解 prototype 参考博客 
        https://www.cnblogs.com/loveyoume/p/6112044.html#4132464  
        */
        function simpleTestPrototype(){
            //  W3C  上认为 prototype 是对象的属性 这是一个错误的说法 
            // prototype 是函数的一个属性是函数的原型对象 
            var  ob ={
                name:"Hello",
                age:20
            }

            alert(JSON.stringify(ob));
            alert(JSON.stringify(ob.prototype))
        }

        function func(){

        }

        function funPrototype(){
            alert(func.prototype)
            func.prototype.name = "prototype是函数的的属性，本质是函数的原型对象"
            alert(JSON.stringify(func.prototype))
        }
        
         //给函数的属性prototype赋予一个方法get
        func.prototype.get = function (value){
                return value;  //很简单，你给我什么我就输出什么
        }

        // 在prototype 方法中添加相应的方法 这种和native 中的运行时类似 
        function  testAddMethodInPrototype(){
            var ob1 = new func()
            alert(ob1.get("Hello De Long "))
        }

        /*
         特殊情况
          Array.prototype是一个数组
　　　　   String.prototype是一个字符串
　　　　   Object.prototype是一个对象
　　      这三个特殊例子，不像构造函数的prototype一样。详情请看下一篇文章。
        */

        // 参考地址  https://www.cnblogs.com/loveyoume/p/6139681.html
        // 下面介绍一下 主要的应用场景 
        // 测试一下新的 应用场景
        function testCallPrototype(){

        }

        function funcTwo(){
            // 这里面我们看到 slice 方法其实是Array 的具体对象来调用的 这个相当于 一个类方法  
            var args = Array.prototype.slice.call(arguments,1);    // 对这段函数的理解要分不同的区段 
        }

        //  slice(start[,end])是js的一个原生数组函数，作用是获取数组中从start下标开始到end下标结束的元素。举个例子：
        function testSlice(){
            var arr1 = [2,3,'1',4,5,'j','i'];
            alert(arr1.slice(1))   // 这里alert的是3,'l',4,5,'j','i'，你自己可以试一下

            // 这种方式其实是Js 内置的Array 他有prototype 的原因 
            var  arr2 = new Array()  
            var  newObj = newObject()   // 这是第二种方式 
        }

        // 
        function newObject(){
            return new Array()
        }

        // 测试数组的的 slice 方法 会直接报错 
        function testArraySlice(){
            alert(Array.slice());  //Uncaught TypeError: Array.slice is not a function
        }


        /*
        上面生成数组的方式和下面构造函数生成对象的方式是不是很相似？没错，js中function和Array都是可以通过new来生成对象这样的东西，
        这说明了什么呢？你看Array()和func()是不是很像？是不是最后面都有一对圆括号？是就对了，呵呵，说了这么多，我只是想揭露一样东西，这个东西就是，
        假如我猜的的没有错的话，Array()这个东西其实是js一个·····构造数组的内置函数····，不然，可以用new Array()的方式生存数组的方式就说不过去了是吧？
        */
        function fourFunction(){

            /*
            是这样子的，Arguments获取func函数实参列表，生成一个对象传递给call()函数，call函数又把Arguments生成的对象传递给Array.prototype这个空数组，
            把第二个参数···1···传递给slice函数，然后，call就让引用它的函数slice执行slice(1)，所以slice就从1下标开始获取数组的元素了，
            而这个数组的元素其实就是Arguments的元素，因此，这个函数func的作用就是获取下标为1开始的所有实参。不相信，你自己可以执行一下上面的函数。
            */
            var args = Array.prototype.slice.call(arguments,1)
            return args;
        }

        function testFourFunction(){
            alert(fourFunction(0,1,2,3,4))   // 给 fourFunction 函数传实参   输出 1，2，3，4
        }

        // 拓展方法 
        function testProto(){

            //给数组原型增加一个打乱数组的函数  只在当前的脚本中的有效
            Array.prototype.shuffle=function(){
            var value = this.valueOf(),len = this.length,temp,key;
            while(len--){
            //随机生成数组的下标
            key = Math.floor(Math.random()*len);
            //temp为中间交换变量
            temp = value[key];
            value[key] = value[len];
            value[len] = temp;
            }
            return value;
            }
            var arr1 = [0,1,2,3,4,5,6,7,8,9];
            var arr2 = ['a','b','c','d','e','f'];    
            alert(JSON.stringify(arr1.shuffle()));
            alert(JSON.stringify(arr2.shuffle()));
        }

        // 第二波测试
        function fun(){
            // 注意这里并没有返回值 
        }

        function testProtoTwo(){
            fun.prototype.name = '小东';
            fun.prototype.arr = [1,2,3,4];//这里的属性可以是数组，也可以是对象
            var ob1 = new fun();
            var ob2 = new fun();
            alert(JSON.stringify(ob1.name));
            alert(JSON.stringify(ob2.arr));
        }

        /*
        应用3：
　　    实现原型继承；  类似于 我们里面讲的这个继承 挺有意思 
        */
        function P1(){
    
        }
        function P2(){
    
        }

        function testProtoThree(){
                //原型对象增加属性和方法
            P2.prototype.name = 'P2"s name';
            P2.prototype.get=function(value){
                    return value;
            }
            //实例化P2构造函数的一个对象
            var obp2 = new P2();//这个对象应该包含所有原型对象的属性和方法
            //给P1的原型对象赋值一个对象，相当于P1继承了obp2的所有属性和方法
            P1.prototype = obp2;//这个式子，简单来讲就类似于a = b, b赋值给a这个总该明白吧？  
            //调用P1从obp2继承过来的get函数
            alert(P1.prototype.get('out"s name'));
            //展示P1从obp2继承过来的name属性
            alert(P1.prototype.name);
            //用构造函数P1实例化一个obp1对象
            var obp1 = new P1();
            //P1的原型对象prototype既然已经继承了obp2的所有属性和函数，那么依据P1所实例化出来的对象也都有obp2的属性和函数了
            alert(obp1.get('obp1"s name'));
        }
       


    </script>
</head>
<body>
    <button onclick="simpleTestPrototype()">测试prototype 并不是对象的属性</button>
    <button onclick="funPrototype()">测试prototype 是函数的属性</button>
    <button onclick="testAddMethodInPrototype()">使用prototype 添加了一个新的函数</button>  
    <button onclick="testSlice()">测试Array 的slice</button>  
    <button onclick="testArraySlice()">直接调用 Array的 slice</button>  
    <button onclick="testFourFunction()">测试 call 函数</button> 
    <button onclick="testProto()">测试 testProto</button> 
    <button onclick="testProtoTwo()">测试 testProtoTwo</button> 
</body>
</html>